import type { IIdentified } from "~/models/Project/Project";
import { PSlot } from "./prototype";
import { PSlotType } from "./type";
import { oGraph, Graph } from "../graph";
import type { IDeserializable } from "~/models/serialize/IDeserializable";
import { oStorage } from "~/models/Project";
import { Deserializer } from "~/models/serialize/Deserializer";
import { Dzer } from "~/models/serialize";
import { symbolsInUsing, TypeTemplate, updateTemplateByComparison, updateTemplateDirect, usedTemplate } from "~/models/types/type_template";
import type { DataType } from "~/models/types/type";
import type { Value } from "~/models/value/Value";

/** 连接槽的状态 */
export enum GraphSlotState {

  /** 节点空置 */
  Disconnected,

  /** 节点已连接 */
  Connected,

  /** 节点写入了常量 */
  Written,
}

/** 连接槽的类型 */
export enum GraphSlotType {

  /** 流程槽 */
  Process,

  /** 数据槽 */
  Data
}


/**
 * Trainer Graph Instance Slot
 * 节点的连接槽，可以：
 * 1. 空置
 * 2. 连接节点
 * 3. 写入常量
 */
export class ISlot implements IIdentified, IDeserializable {

  id: string = randomName()
  className: string = 'ISlot'

  static {
  }

  prototype: PSlot

  constructor(
    prototype: PSlot = new PSlot(),
    mount = false
  ) {
    this.prototype = Dzer.clone(prototype)

    // 如果有默认值，则写入
    if (prototype.defaultValue !== undefined) {
      this.data = prototype.defaultValue
    }
    if (mount) {
      oStorage.value!.slots.push(this)
    }
    
  }

  _peersId: string[] = []

  public get peers(): ISlot[] {
    return this._peersId.map(x => oStorage.value!.slots.find(y => x === y.id)!)
  }

  public get peer(): ISlot | undefined {
    return this.peers[0]
  }

  public compatibleWith(another?: ISlot): boolean {
    return this.prototype.compatibleWith(another?.prototype, true)
  }

  /**
   * 槽之间的双向连接
   * @param s 另一个槽
   */
  public connect(another?: ISlot) {

    if (!another || !this.compatibleWith(another)) {
      return
    }

    // ProcessOut 只能有确认的一个 peer。
    let processOutMulticonected = undefined as undefined | ISlot
    let abortedPeer = undefined as undefined | ISlot
    for (const slot of [this, another]) {
      const p = slot.prototype
      if (p.type === PSlotType.Process && p.direction === 'out') {
        console.log('ProcessOut need disconnect');
        
        processOutMulticonected = slot
        abortedPeer = slot.peer
        slot.disconnect()
        console.log('processOutMulticonected', processOutMulticonected);
        console.log('abortedPeer', abortedPeer);
      }
    }

    // 是否进行插入连接？
    const doinsert = true
    if (doinsert) {
      const needInsertedLocation = processOutMulticonected === this ? another : this
      const needInsertedNode = oGraph.value?.nodes.find(x => x.slots.includes(needInsertedLocation))
      abortedPeer?.connect(needInsertedNode?.getProcessOut())
    }

    // InData 只能有确认的一个 peer。
    for (const slot of [this, another]) {
      const p = slot.prototype
      if (p.type === PSlotType.Data && p.direction === 'in') {
        slot.disconnect()
      }
    }

    // 自动双向连接
    if (!this._peersId.includes(another.id)) {
      this._peersId.push(another.id)
    }
    if (!another._peersId.includes(this.id)) {
      another._peersId.push(this.id)
    }
  }

  public disconnect() {
    this.peers.forEach(peer => {
      remove(peer._peersId, x => x === this.id)
    })
    this._peersId = []
  }

  /** 修改原型 */
  update(newProto: PSlot) {
    const oldProto = this.prototype

    // 不支持的修改
    if (
      oldProto.direction !== newProto.direction
      || oldProto.type !== newProto.type
    ) {
      console.error(oldProto);
      console.error(newProto);
      throw new Error(`不支持的修改`)
    }

    // 应用新原型
    this.prototype = newProto

    // 如果修改了数据类型，还要断连
    if (!oldProto.dataType?.is(newProto.dataType)) {
      this.repairConnections()
    }
  }

  /**
   * 移除所有错误的连接
   */
  repairConnections() {
    this.peers.forEach(peer => {
      if (!this.prototype.compatibleWith(peer.prototype)) {
        remove(this._peersId, x => x === peer.id)
        remove(peer._peersId, x => x === this.id)
      }
    })
  }

  public state: GraphSlotState = GraphSlotState.Disconnected
  public x: number = 0
  public y: number = 0

  // 如果是数据类型的槽，并且使用常量写入
  public data?: Value

  get inUse(): boolean {
    return this.peer !== undefined || this.data !== undefined
  }

  findparent(Graph: Graph) {
    return Graph.nodes.find(x => x.slots.includes(this))
  }

  /** 在使用模板 */
  get usingTemplate(): boolean {
    const _p =  this.prototype
    if (_p.type === PSlotType.Process) { return false }
    const _t = _p.dataType!
    return usedTemplate(_t)
  }

  get symbolsInUsing(): string[] {
    const _p =  this.prototype
    if (_p.type === PSlotType.Process) { return [] }
    const _t = _p.dataType!
    return symbolsInUsing(_t)
  }

  updateTCompare(t: DataType) {
    if (!this.usingTemplate) {
      throw "在调用模板更新时，没有判断是否使用模板。(实际上没有使用)"
    }
    return updateTemplateByComparison(this.prototype.dataType!, t)
  }

  updateTDirect(t: DataType) {
    if (!this.usingTemplate) {
      throw "在调用模板更新时，没有判断是否使用模板。(实际上没有使用)"
    }
    updateTemplateDirect(this.prototype.dataType!, t)
  }
}

// Prototype的部分已经写好了，现在是Instance的部分